package com.jinly.ryder.log.service.impl;


import com.alibaba.fastjson.JSON;
import com.jinly.ryder.common.error.SystemErrorType;
import com.jinly.ryder.common.service.SystemSettingService;
import com.jinly.ryder.common.service.model.setting.TencentSetting;
import com.jinly.ryder.log.client.Cls2017Client;
import com.jinly.ryder.log.client.dto.ExtractRuleDTO;
import com.jinly.ryder.log.client.dto.LogsetDTO;
import com.jinly.ryder.log.client.dto.MachineGroupDTO;
import com.jinly.ryder.log.client.dto.TopicInfoDTO;
import com.jinly.ryder.log.service.LogInstallService;
import com.jinly.ryder.manage.service.AppInfoService;
import com.jinly.ryder.manage.service.AppInstanceService;
import com.jinly.ryder.manage.service.model.AppInfo;
import com.jinly.ryder.manage.service.model.AppInstance;
import com.jinly.ryder.manage.service.model.AppLogType;
import com.jinly.ryder.ssh.service.SshCommandService;
import com.tencentcloudapi.cls.v20201016.ClsClient;
import com.tencentcloudapi.cls.v20201016.models.*;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class LogInstallServiceImpl implements LogInstallService {

    private final SystemSettingService systemSettingService;
    private final AppInstanceService appInstanceService;
    private final AppInfoService appInfoService;
    private final SshCommandService sshCommandService;

    private Cls2017Client cls2017Client;
    private ClsClient clsClient;
    private TencentSetting tencentSetting;


    @Override
    public void install(String appName) {
        AppInfo appInfo = appInfoService.getAppInfo(appName);
        if (appInfo.getLogType() != AppLogType.CLS) return;
        List<String> ips = appInstanceService.list(appName, null).stream().map(AppInstance::getHost).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(ips)) return;
        // 创建日志集
        checkClient();
        LogsetDTO logset = cls2017Client.queryLogsetList().stream().filter(l -> l.getLogsetName().equals(tencentSetting.getLogsetName()))
                .findAny().orElseGet(() -> this.createLogset(tencentSetting.getLogsetName()));
        // 创建日志主题
        TopicInfoDTO topicInfoDTO = cls2017Client.queryTopicsList(logset.getLogsetId()).stream().filter(t -> t.getTopicName().equals(appName))
                .findAny().orElseGet(() -> this.createTopic(logset.getLogsetId(), appInfo));
        // 创建索引
        installTopicIndex(topicInfoDTO.getTopicId());

        // 创建机器组
        MachineGroupDTO machineGroupDTO = cls2017Client.queryMachineGroupList().stream().filter(m -> m.getGroupName().equals(appName))
                .findAny().orElseGet(() -> this.createMachineGroup(appName, ips));
        machineGroupDTO.setIps(ips);
        cls2017Client.updateMachineGroup(machineGroupDTO);
        // 主题绑定机器组
        topicInfoDTO.setMachineGroups(List.of(machineGroupDTO.getGroupId()));
        cls2017Client.topicMachineGroup(topicInfoDTO);
    }

    private void installTopicIndex(String topicId) {
        DescribeIndexRequest req = new DescribeIndexRequest();
        req.setTopicId(topicId);
        try {
            DescribeIndexResponse resp = clsClient.DescribeIndex(req);
            log.info("DescribeIndexResponse:{}", JSON.toJSONString(resp));
            if (!resp.getStatus()) {
                CreateIndexRequest createIndexReq = new CreateIndexRequest();
                createIndexReq.setTopicId(topicId);
                createIndexReq.setStatus(true);
                RuleInfo rule = new RuleInfo();
                FullTextInfo fullTextInfo = new FullTextInfo();
                fullTextInfo.setCaseSensitive(false);
                fullTextInfo.setTokenizer("@&()='\",;:<>[]{}/ \\n\\t\\r");
                rule.setFullText(fullTextInfo);
                RuleKeyValueInfo ruleKeyValueInfo = new RuleKeyValueInfo();
                ruleKeyValueInfo.setCaseSensitive(false);
                KeyValueInfo[] keyValues = new KeyValueInfo[2];
                keyValues[0] = new KeyValueInfo();
                keyValues[0].setKey("level");
                ValueInfo v0 = new ValueInfo();
                v0.setType("text");
                v0.setSqlFlag(true);
                keyValues[0].setValue(v0);

                keyValues[1] = new KeyValueInfo();
                keyValues[1].setKey("content");
                ValueInfo v1 = new ValueInfo();
                v1.setType("text");
                v1.setSqlFlag(true);
                keyValues[1].setValue(v1);

                ruleKeyValueInfo.setKeyValues(keyValues);
                rule.setKeyValue(ruleKeyValueInfo);
                createIndexReq.setRule(rule);
                clsClient.CreateIndex(createIndexReq);
            }
        } catch (Exception e) {
            log.error("创建日志主题索引失败", e);
        }
    }

    @Override
    public void installLogListener(String ip) {
        tencentSetting = systemSettingService.getSettingItem(TencentSetting.class);
        String checkCommand = "/etc/init.d/loglistenerd check";
        List<String> result = sshCommandService.command(ip, 22, checkCommand);
        if (checkCommandResult(result, "running ok")) {
            return;
        }
        //安装
        String installCommand = "wget https://mirrors.tencent.com/install/cls/loglistener-linux-x64-2.5.8.tar.gz  && tar -zxvf loglistener-linux-x64-2.5.8.tar.gz -C /usr/local && cd /usr/local/loglistener-2.5.8/tools && ./loglistener.sh install";
        result = sshCommandService.command(ip, 22, installCommand);
        checkCommandResult(result, "success");
        //初始化
        String initCommand = "cd /usr/local/loglistener-2.5.8/tools && ./loglistener.sh init -secretid " + tencentSetting.getSecretId() + " -secretkey " + tencentSetting.getSecretKey() + " -region ap-shanghai";
        result = sshCommandService.command(ip, 22, initCommand);
        if (!checkCommandResult(result, "init success")) {
            SystemErrorType.INTERNAL_SERVER_ERROR.throwError("安装失败");
        }
        //启动
        String startCommand = "/etc/init.d/loglistenerd start";
        result = sshCommandService.command(ip, 22, startCommand);
        if (!checkCommandResult(result, "success")) {
            SystemErrorType.INTERNAL_SERVER_ERROR.throwError("启动失败");
        }
    }

    private boolean checkCommandResult(List<String> result, String hit) {
        boolean checkResult = false;
        for (String s : result) {
            if (s.indexOf(hit) != -1) {
                checkResult = true;
            }
        }
        return checkResult;
    }

    private MachineGroupDTO createMachineGroup(String appName, List<String> ips) {
        MachineGroupDTO machineGroupDTO = new MachineGroupDTO();
        machineGroupDTO.setGroupName(appName);
        machineGroupDTO.setType("ip");
        machineGroupDTO.setIps(ips);
        MachineGroupDTO r = cls2017Client.createMachineGroup(machineGroupDTO);
        machineGroupDTO.setGroupId(r.getGroupId());
        log.info("创建腾讯日志机器组:{}", machineGroupDTO);
        return machineGroupDTO;
    }

    private TopicInfoDTO createTopic(String logsetId, AppInfo appInfo) {
        TopicInfoDTO topicInfoDTO = new TopicInfoDTO();
        topicInfoDTO.setLogsetId(logsetId);
        topicInfoDTO.setTopicName(appInfo.getAppName());
        String path = appInfo.getLogSetting().getRootPath() + "/**/" + appInfo.getLogSetting().getFileName();
        topicInfoDTO.setWildPath(path.replaceAll("\\/\\/", "/"));
        topicInfoDTO.setLogType("multiline_fullregex_log");
        topicInfoDTO.setIndex(true);
        ExtractRuleDTO rule = new ExtractRuleDTO();
        rule.setBeginningRegex("\\d+-\\d+-\\d+\\s\\d+:\\d+:\\d+\\.\\d+\\s.*");
        rule.setLogRegex("(\\d+-\\d+-\\d+\\s\\S+)\\s+(\\w+)\\s(\\d+)\\s---\\s\\[([^\\]]+)\\]\\s(.*)");
        rule.setTimeFormat("%Y-%m-%d %H:%M:%S.%f");
        rule.setTime_key("time");
        rule.setKeys(List.of("time", "level", "pid", "thread", "content"));
        topicInfoDTO.setExtractRule(rule);
        TopicInfoDTO r = cls2017Client.createTopic(topicInfoDTO);
        topicInfoDTO.setTopicId(r.getTopicId());
        log.info("创建腾讯日志主题:{}", topicInfoDTO);
        return r;
    }

    private LogsetDTO createLogset(String logsetName) {
        LogsetDTO logsetDTO = new LogsetDTO();
        logsetDTO.setLogsetName(logsetName);
        logsetDTO.setPeriod(90);
        LogsetDTO r = cls2017Client.createLogset(logsetDTO);
        logsetDTO.setLogsetId(r.getLogsetId());
        log.info("创建腾讯日志集:{}", logsetDTO);
        return r;
    }

    private synchronized void checkClient() {

        try {
            tencentSetting = systemSettingService.getSettingItem(TencentSetting.class);
            if (cls2017Client == null) {
                cls2017Client = new Cls2017Client(tencentSetting.getSecretId(), tencentSetting.getSecretKey());
            }
            if (clsClient == null) {
                Credential cred = new Credential(tencentSetting.getSecretId(), tencentSetting.getSecretKey());
                HttpProfile httpProfile = new HttpProfile();
                httpProfile.setEndpoint("cls.tencentcloudapi.com");
                ClientProfile clientProfile = new ClientProfile();
                clientProfile.setHttpProfile(httpProfile);
                clsClient = new ClsClient(cred, tencentSetting.getRegion(), clientProfile);
            }
        } catch (Exception e) {
            log.error("初始化腾讯云客户端失败:{}", e.getMessage());
            throw SystemErrorType.INTERNAL_SERVER_ERROR.buildError();
        }
    }


}
